package api

import (
	"gitee.com/gopher2011/gva/server/dao"
	"gitee.com/gopher2011/gva/server/util"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"github.com/mojocn/base64Captcha"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"time"
)

// sysUserApi 管理对象。
var SysUser = &sysUserApi{}

type sysUserApi struct{}

var store = base64Captcha.DefaultMemStore

// @Tags 用户管理
// @Summary 生成验证码
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"验证码获取成功"}"
// @Router /base/captcha [post]
func (it *sysUserApi) Captcha(r *gin.Context) {
	//字符,公式,验证码配置
	driver := base64Captcha.NewDriverDigit(dao.Cfg.Captcha.ImgHeight, dao.Cfg.Captcha.ImgWidth, dao.Cfg.Captcha.KeyLong, 0.7, 80)
	// 生成默认数字的driver
	cp := base64Captcha.NewCaptcha(driver, store)
	id, b64s, err := cp.Generate()
	if err != nil {
		FailWithMsg(r,"验证码获取失败")
		return
	}
	Ok(r,dao.SysCaptchaResponse{
		CaptchaId: id,
		PicPath:   b64s})
}

// @Tags 用户管理
// @Summary 用户注册账号
// @Produce  application/json
// @Param data body dao.Register true "用户名, 昵称, 密码, 角色ID"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"注册成功"}"
// @Router /register [post]
func (it *sysUserApi) Register(r *gin.Context) {
	var ptr dao.Register
	//省略对接口请求参数的验证
	if err := r.ShouldBind(&ptr);err != nil {
		Err(r,err)
		return
	}
	user := dao.SysUser{Username: ptr.Username,NickName: ptr.NickName,Password: ptr.Password,HeaderImg: ptr.HeaderImg,AuthorityId: ptr.AuthorityId}
	err := dao.SysUserDao.Register(user)
	if err != nil {
		FailWithMsg(r,"注册失败")
		return
	}
	Ok(r,nil)
}

// @Tags 用户管理
// @Summary 用户登录
// @Produce  application/json
// @Param data dao.Login true "用户名, 密码, 验证码"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"登陆成功"}"
// @Router /login [post]
func (it *sysUserApi) Login(r *gin.Context) {
	var ptr dao.Login
	r.ShouldBind(&ptr)
	//获取验证码
	if store.Verify(ptr.CaptchaId, ptr.Captcha, true) {
		user,err := dao.SysUserDao.Login(ptr.Username, util.Md5([]byte(ptr.Password)))
		if err != nil {
			Err(r,err)
			return
		}
		// 登陆成功,则生成 token
		it.genToken(r, *user)
	} else {
		FailWithMsg(r,"验证码错误")
	}
}

// 登录以后签发jwt
func (it *sysUserApi) genToken(r *gin.Context, user dao.SysUser) {
	// 唯一签名
	j := JWT{SigningKey: []byte(dao.Cfg.JWT.SigningKey)}
	claims := dao.JwtClaims{ //这是要序列化到token中的信息(即:被token保护的信息)
		UUID:        user.Uuid,
		ID:          user.Id,
		NickName:    user.NickName,
		Username:    user.Username,
		AuthorityId: user.AuthorityId,
		BufferTime:  dao.Cfg.JWT.BufferTime, // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 1000,                        // 签名生效时间
			ExpiresAt: time.Now().Unix() + dao.Cfg.JWT.ExpiresTime, // 过期时间,配置文件中默认 7天
			Issuer:    "gva",
		},
	}
	token, err := j.CreateToken(claims)
	if err != nil {
		FailWithMsg(r,"获取token失败")
		return
	}
	// 如果没有开启多点登陆
	if !dao.Cfg.System.UseMultipoint {
		OkWithDetailed(r,"登陆成功!",dao.LoginResponse{
			User:      user,
			Token:     token,
			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
		})
		return
	}
	// 输入当前登陆的用户名,去redis中取出对应的 jwt
	if jwtStr, err := dao.JwtBlackListDao.GetRedisJwt(user.Username); err == redis.Nil {
		// 以用户名作为 key,token 作为 value,将 用户名<==>token,这种映射关系保存到 redis中!
		if err := dao.JwtBlackListDao.SetRedisJwt(token, user.Username); err != nil {
			FailWithMsg(r,"设置登陆状态失败!")
			return
		}
		// 如果设置登陆状态成功
		OkWithDetailed(r,"登陆成功!",dao.LoginResponse{
			User:      user,
			Token:     token,
			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
		})
	}else if err != nil {
		FailWithMsg(r,"设置登录状态失败")
	}else {
		// 将 redis 中的 jwt 拉入黑名单
		if rows := dao.JwtBlackListDao.Insert(dao.JwtBlackList{Token: jwtStr}); rows <=0 {
			FailWithMsg(r,"拉黑jwt失败!")
			return
		}
		if err := dao.JwtBlackListDao.SetRedisJwt(token, user.Username); err != nil {
			FailWithMsg(r,"设置登录状态失败")
			return
		}
		OkWithDetailed(r,"登录成功",dao.LoginResponse{
			User:      user,
			Token:     token,
			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
		})
	}
}

// @Tags 用户管理
// @Summary 用户修改密码
// @Security ApiKeyAuth
// @Produce  application/json
// @Param data body dao.ChangePassword true "用户名, 原密码, 新密码"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
// @Router /user/changePassword [put]
func (it *sysUserApi) ChangePassword(r *gin.Context) {
	var ptr dao.ChangePassword
	r.ShouldBind(&ptr) //省略对请求参数的验证(gin中引用的请求参数验证库不好用!!!)
	if err := dao.SysUserDao.ChangePassword(ptr);err !=nil {
		Err(r,err)
		return
	}
	Ok(r,nil)
}

// @Tags 用户管理
// @Summary 分页获取用户列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dao.Page true "页码, 每页大小"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /user/getUserList [post]
func (it *sysUserApi) GetUserList(r *gin.Context) {
	var ptr dao.Page
	r.ShouldBind(&ptr)
	list, i, err := dao.SysUserDao.GetUserInfoList(ptr)
	if err != nil {
		FailWithMsg(r,"获取失败")
		return
	}
	OkWithDetailed(r,"获取成功",dao.PageResult{
		List:     list,
		Total:    i,
		Page:     ptr.PageNum,
		PageSize: ptr.PageSize,
	})
}

// @Tags 用户管理
// @Summary 设置用户权限
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dao.SetUserAuth true "用户UUID, 角色ID"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置用户权限成功"}"
// @Router /user/setUserAuthority [post]
func (it *sysUserApi) SetUserAuthority(r *gin.Context) {
	var ptr dao.SetUserAuth
	r.ShouldBind(&ptr)
	if rows :=dao.SysUserDao.SetAuth(ptr);rows <=0 {
		FailWithMsg(r,"设置用户权限失败")
		return
	}
	Ok(r,nil)
}

// @Tags 用户管理
// @Summary 删除用户
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dao.GetById true "用户ID"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /user/deleteUser [delete]
func (it *sysUserApi) DeleteUser(r *gin.Context) {
	var ptr dao.GetById
	if err := r.ShouldBind(&ptr);err !=nil {
		Err(r,err)
		return
	}
	// 要彻底的删除指定用户信息,则先需删除缓存在 http.Request 中的用户信息!
	jwtId := it.getUserId(r)
	if jwtId == ptr.Id {
		FailWithMsg(r,"删除失败!")
		return
	}
	if rows :=dao.SysUserDao.Delete(ptr.Id);rows <=0 {
		FailWithMsg(r,"删除失败")
		return
	}
	Ok(r,nil)
}

// 从Gin的Context中获取从jwt解析出来的用户ID
func (it *sysUserApi) getUserId(r *gin.Context) int {
	if claims, exists := r.Get("claims"); !exists {
		// "从Gin的Context中获取从jwt解析出来的用户ID失败, 请检查路由是否使用jwt中间件"
		return 0
	} else {
		waitUse := claims.(*dao.JwtClaims)
		return waitUse.ID
	}
}

// @Tags 用户管理
// @Summary 设置用户信息(更新用户信息)
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dao.SysUser true "ID, 用户名, 昵称, 头像链接"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
// @Router /user/setUserInfo [put]
func (it *sysUserApi) SetUserInfo(r *gin.Context) {
	var ptr dao.SysUser
	if err := r.ShouldBind(&ptr); err != nil {
		Err(r,err) // 这里省略请求参数验证
		return
	}
	if rows := dao.SysUserDao.Update(ptr); rows <=0 {
		FailWithMsg(r,"设置用户信息失败")
		return
	}
	OkWithDetailed(r,"设置成功",gin.H{"userInfo": ptr})
}

// 从请求中返回 authorityId
func getUserAuthorityId(c *gin.Context) string {
	if claims, exist := c.Get("claims"); !exist {
		dao.Log.Error("从gin的Context中获取从jwt解析出来的用户UUID失败,请检查路由是否使用jwt中间件")
		return ""
	} else {
		waitUse := claims.(*dao.JwtClaims)
		return waitUse.AuthorityId
	}
}

// @Summary 获取配置文件内容
// @Router /system/getSystemConfig [post]
func GetSystemConfig(c *gin.Context) {
	OkWithDetailed(c,"获取成功",dao.SysConfigResponse{Config:dao.Cfg})
}

// @Tags 系统管理
// @Summary 设置配置文件内容
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body dao.System true "系统配置"
// @Success 200 {object} Response
// @Router /system/setSystemConfig [post]
func SetSystemConfig(c *gin.Context) {
	var ptr dao.System
	if err := c.ShouldBind(&ptr); err !=nil {
		FailWithMsg(c,err.Error())
		return
	}
	cs := util.StructToMap(ptr.Config)
	for k,v := range cs { // 获取每一个配置项
		dao.Vp.Set(k,v)
	}
	if err := dao.Vp.WriteConfig();err != nil {
		FailWithMsg(c,"设置配置文件内容失败!")
		return
	}
	Ok(c,"设置成功")
}

// @Tags 系统管理
// @Summary 重启系统
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Router /system/reloadSystem [post]
func ReloadSystem(c *gin.Context){
	if runtime.GOOS == "windows" {
		FailWithMsg(c,"系统不支持")
		return
	}
	pid := os.Getpid() //获取当前进程ID
	cmd := exec.Command("kill","-1",strconv.Itoa(pid))
	if err := cmd.Run();err != nil {
		FailWithMsg(c,"重启系统失败")
		return
	}
	Ok(c,"重启系统成功")
}

// @Tags 系统管理
// @Summary 获取服务器信息
// @Router /system/getServerInfo [post]
func GetServerInfo(c *gin.Context){
	if server,err := util.GetServerInfo(); err != nil {
		FailWithMsg(c,"获取服务器信息失败")
		return
	}else {
		OkWithDetailed(c,"获取服务器信息成功",gin.H{"server": server})
	}
}



